/**
 *  julive
 *
 *  Create by songli on 2021/07/07
 *  Copyright © 2021 居理买房网. All rights reserved.
 */

package repository

import (
	"context"
	"errors"
	"fmt"
	"time"

	"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
	"go.uber.org/zap"
	"gorm.io/gorm"

	"git.julive.com/julive/shorturl/pkg/constant"
	utilx "git.julive.com/julive/shorturl/pkg/util"
	"git.julive.com/julive/shorturl/server/model"
	"git.julive.com/micro/pkg/dao"
	"git.julive.com/micro/pkg/redisx"
	"git.julive.com/micro/pkg/util"
)

type ShorturlRepository interface {
	dao.Dao

	CreateShorturl(ctx context.Context, path, url string, maxTry int) (*model.UrlMainShard, int, error)
	GetShorturl(ctx context.Context, path, url string) (*model.UrlMainShard, error)
}

type shorturlRepository struct {
	dao.Dao
}

//NewShorturlRepository 创建用户仓库实例
func NewShorturlRepository() ShorturlRepository {
	repo := &shorturlRepository{
		dao.NewRepository(),
	}
	return repo
}

func (repo *shorturlRepository) CreateShorturl(ctx context.Context, path, url string, maxTry int) (*model.UrlMainShard, int, error) {
	logger := ctxzap.Extract(ctx).With(zap.String("layer", "shorturlRepository"), zap.String("func", "CreateShorturl"))
	urlMain := new(model.UrlMain)
	err := urlMain.SetMainFlag(path)
	if err != nil {
		logger.Error("urlMain.SetMainFlag error", zap.Error(err))
		return nil, maxTry, err
	}
	fmt.Println("shorturlRepository.CreateShorturl", path, url, maxTry)
	var urlMainShard *model.UrlMainShard
	for maxTry > 0 {
		err = repo.writeDB().Table(urlMain.TableName()).Omit("UrlMainFlag").Create(urlMain).Error
		if err != nil {
			logger.Error("urlMain.Create error", zap.Error(err))
			maxTry--
			continue
		}

		shareId := urlMain.Id
		urlMainShard = new(model.UrlMainShard)
		urlMainShard.ShortUrl = utilx.Base62encode(shareId)
		urlMainShard.ShardId = shareId
		urlMainShard.LongUrl = url
		urlMainShard.UrlMainFlag = urlMain.UrlMainFlag
		urlMainShard.CreateDatetime = time.Now().Unix()

		err = repo.writeDB().Table(urlMainShard.TableName()).Omit("UrlMainFlag", "ShardId").Create(urlMainShard).Error
		if err != nil {
			logger.Error("urlMainShard.Create error", zap.Error(err))
			maxTry--
			continue
		}
		break
	}

	return urlMainShard, maxTry, err
}

func (repo *shorturlRepository) GetShorturl(ctx context.Context, path, url string) (*model.UrlMainShard, error) {
	m := &model.UrlMainShard{}
	key := util.AppendString("jl_shorturl_", path, "_", url)

	err := redisx.HGetAll(ctx, repo.Redis(), key, m)
	if err == nil {
		if m.Id == 0 {
			return nil, nil
		}
		return m, nil
	}

	m, err = repo.FindShorturl(ctx, path, url)
	if err != nil {
		return nil, err
	}

	if m != nil {
		expire := constant.CacheDay1 + util.RandDuration(3600)
		redisx.SetHashTable(ctx, repo.Redis(), key, m, expire)
		return m, nil
	} else {
		m = &model.UrlMainShard{Id: 0}
		expire := constant.CacheMinute5 + util.RandDuration(120)
		redisx.SetHashTable(ctx, repo.Redis(), key, m, expire)
		return nil, nil
	}
}

func (repo *shorturlRepository) FindShorturl(ctx context.Context, path, url string) (*model.UrlMainShard, error) {
	logger := ctxzap.Extract(ctx).With(zap.String("layer", "shorturlRepository"), zap.String("func", "FindShorturl"))
	urlMainShard := &model.UrlMainShard{}
	err := urlMainShard.SetMainFlag(path)
	if err != nil {
		logger.Error("urlMainShard.SetMainFlag error", zap.Error(err))
		return nil, err
	}

	shareId := utilx.Base62decode(url)
	urlMainShard.ShardId = int32(shareId)

	err = repo.readDB().Table(urlMainShard.TableName()).Where("short_url = ?", url).First(urlMainShard).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, err
	}
	return urlMainShard, nil
}

func (repo *shorturlRepository) readDB() *gorm.DB {
	return repo.ReadDB(constant.DBName)
}

func (repo *shorturlRepository) writeDB() *gorm.DB {
	return repo.WriteDB(constant.DBName)
}
